package cn.touna.jss.common.aspect;

import cn.touna.jss.common.cache.HbaseComponent;
import cn.touna.jss.common.entity.CreditQueryRequest;
import cn.touna.jss.common.filter.entity.WrappedHttpServletRequest;
import cn.touna.jss.common.mq.KafkaComponent;
import cn.touna.jss.common.util.DateUtils;
import cn.touna.jss.common.util.JssConstant;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.adapter.ApiUtils;
import cn.touna.jss.modules.credit.service.AuthHistoryService;
import cn.touna.jss.modules.credit.service.CreditQueryService;
import cn.touna.risk.api.domain.Response;
import cn.touna.risk.api.entity.RequestEntity;
import cn.touna.risk.api.exception.BusinessException;
import cn.touna.risk.api.exception.ServiceException;
import cn.touna.risk.api.utils.IntfTypeUtil;
import cn.touna.risk.api.utils.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.touna.loan.log.service.TracerContextUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

/**
 * \* User: MeiZhongHao
 * \* Date: 2018-06-12
 * \* Time: 19:13
 * \* Description:
 * \
 */
@Component
public class ApiMappingRecordService {

    private static final Logger logger = LogManager.getLogger(ApiMappingRecordService.class);

    @Autowired
    private KafkaComponent kafkaComponent;

    @Autowired
    protected HbaseComponent hbaseComponent;

    @Autowired
    private CreditQueryService creditQueryService;

    @Autowired
    private AuthHistoryService authHistoryService;

    /***
     * 根据请求参数，存储请求接口记录
     * @param pjp
     * @param result
     * @return 根据
     * @throws Throwable
     */
    public Object audit(JoinPoint pjp, Object result) throws Throwable {
        Response response = new Response();
        CreditQueryRequest history = null;
        String apiMethod = null;
        Method targetMethod = getMethodByPjp(pjp);
        ApiMapping apiMapping = (ApiMapping) targetMethod.getAnnotation(ApiMapping.class);
        try {
            if (apiMapping.recordHistory()) {
                history = initRequestRecordHistory(pjp, apiMapping, null);
            }
            response = ApiUtils.hanldReturnResult(result);
            // 返回值是否使用缓存
            if (apiMapping.recordHistory() && response.getSrc() == 1 && history != null)
                history.setCacheQueryAble(JssConstant.Y);

            return response;
        } catch (BusinessException e) {
            logger.error(e.getMessage(),e);
            response = ApiUtils.handError(e, apiMethod);
            return response;
        } catch (ServiceException e) {
            logger.error(e.getMessage(),e);
            response = ApiUtils.handError(e, apiMethod);
            return response;
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            response = ApiUtils.handError(e, apiMethod);
            return response;
        } finally {
            if (apiMapping.recordHistory())
                kafkaComponent.sendQueryHistory(history, response);
        }
    }

    public void saveHistory(JoinPoint pjp, Object result, HttpServletRequest request) throws Throwable {
        Response response = new Response();
        CreditQueryRequest history = null;
        String apiMethod = null;
        Method targetMethod = getMethodByPjp(pjp);
        ApiMapping apiMapping = (ApiMapping) targetMethod.getAnnotation(ApiMapping.class);
        try {
            if (apiMapping.recordHistory()) {
                history = initRequestRecordHistory(pjp, apiMapping, request);
            }
            response = ApiUtils.hanldReturnResult(result);
            // 返回值是否使用缓存
            if (apiMapping.recordHistory() && response.getSrc() == 1 && history != null)
                history.setCacheQueryAble(JssConstant.Y);
        } catch (BusinessException e) {
            logger.error(e.getMessage(), e);
            response = ApiUtils.handError(e, apiMethod);
        } catch (ServiceException e) {
            logger.error(e.getMessage(), e);
            response = ApiUtils.handError(e, apiMethod);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            response = ApiUtils.handError(e, apiMethod);
        } finally {
            if (apiMapping.recordHistory()) {
                saveQueryHistory(history, response);
                kafkaComponent.sendQueryHistory(history, response);
            }

        }
    }


    /***
     * @param pjp
     * @return
     */
    private Method getMethodByPjp(JoinPoint pjp) {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        return methodSignature.getMethod();
    }

    /*
     * @Author wuwc
     * @Description //映射请求头的参数值
     * @Date 16:23 2018/12/17
     * @Param [request]
     * @return com.alibaba.fastjson.JSONObject
     **/
    private JSONObject getRequestParams(HttpServletRequest request) {
        JSONObject requestParams = null;
        try {
            if (request != null && WrappedHttpServletRequest.class.isAssignableFrom(request.getClass())) {
                requestParams = ((WrappedHttpServletRequest) request).getRequestParams();
                requestParams.put("clientIp", request.getHeader("client_ip"));
                requestParams.put("sysName", request.getHeader("sysName"));
                requestParams.put("productType", request.getHeader("productType"));
                requestParams.put("phaseName", request.getHeader("phaseName"));
                requestParams.put("applyNoType", request.getHeader("applyNoType"));
                requestParams.put("applyNo", request.getHeader("applyNo"));
                requestParams.put("timestamp", request.getHeader("timestamp"));
                return requestParams;
            }
        } catch (IOException e) {
            logger.error("获取请求参数失败，msg:{}", e.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return requestParams;
    }

    private JSONObject getRequestParams() {
        JSONObject requestParams = null;
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            // 本次request请求的参数
            if(attributes == null || attributes.getRequest() == null)
                return null;
            if(attributes.getRequest() instanceof WrappedHttpServletRequest){
                requestParams = ((WrappedHttpServletRequest) attributes.getRequest()).getRequestParams();
                HttpServletRequest request = attributes.getRequest();
                requestParams.put("clientIp", request.getHeader("client_ip"));
                requestParams.put("sysName",request.getHeader("sysName"));
                requestParams.put("productType",request.getHeader("productType"));
                requestParams.put("phaseName",request.getHeader("phaseName"));
                requestParams.put("applyNoType",request.getHeader("applyNoType"));
                requestParams.put("applyNo",request.getHeader("applyNo"));
                //requestParams.put("timestamp",request.getHeader("timestamp"));
                return requestParams;
            }
        } catch (IOException e) {
            logger.error("获取请求参数失败，msg:{}", e.getMessage());
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return null;
    }

    /*
     * @Author wuwc
     * @Description 初始化调用历史请求参数
     * @Date 18:30 2018/12/10
     * @Param [pjp, apiMapping]
     * @return cn.touna.jss.common.entity.CreditQueryRequest
     **/
    private CreditQueryRequest initRequestRecordHistory(JoinPoint pjp, ApiMapping apiMapping, HttpServletRequest request) {
        JSONObject paramsObj = getRequestParams(request);
        // http 请求参数
        RequestEntity requestEntity = null;
        if(paramsObj != null){
            requestEntity = JSONUtil.jsonToObject(paramsObj, RequestEntity.class);
        }else{
            //接口请求在新的线程中，新的线程中没有存,此时需要自己组装参数
            Object[] args = pjp.getArgs();
            Method method = getMethodByPjp(pjp);
            Annotation[][] annos = method.getParameterAnnotations();
            JSONObject obj = new JSONObject();
            for(int i = 0;i<args.length;i++){
                Object arg = args[0];
                if(RequestEntity.class.isAssignableFrom(arg.getClass())){
                    requestEntity = (RequestEntity) arg;
                    break;
                }else{
                    //对于使用注解@RequestParam的参数，实施注入
                    boolean hasAnno = false;
                    for (Annotation anno : annos[i]) {
                        if (anno instanceof RequestParam) {
                            RequestParam requestParam = (RequestParam) anno;
                            obj.put(requestParam.name(),arg);
                            hasAnno = true;
                        }
                    }
                    if(!hasAnno){
                        obj = (JSONObject) JSONObject.toJSON(arg);
                        break;
                    }
                }
            }

            if (requestEntity == null) {
                requestEntity = new RequestEntity();
                String methodStr = apiMapping.value();
                requestEntity.setMethod(methodStr);
                String[] arr = methodStr.split("[.]");
                requestEntity.setServiceName(arr[0]);
                requestEntity.setMethodName(arr[1]);
                requestEntity.setBizType("");
                requestEntity.setClientIp("");
                requestEntity.setSystemName("");
                requestEntity.setParams(obj);
            }
        }
        if (apiMapping.isFile() && requestEntity.getParams() != null) {
            //文件流类型接口需要去除byte[]文件数据保存调用记录
            for (Map.Entry<String, Object> entry : requestEntity.getParams().entrySet()) {
                if(null!=entry.getValue()){
                    if("image".equals(entry.getKey()) || "file".equals(entry.getKey())){
                        entry.setValue(null);
                    }
                }
            }
        }

        String apiMethod = apiMapping.value();
        CreditQueryRequest history = new CreditQueryRequest();
        history.setMethod(apiMethod);
        history.setServiceName(IntfTypeUtil.splitIntfType(apiMethod)[0]);
        history.setMethodName(IntfTypeUtil.splitIntfType(apiMethod)[1]);
        history.setParams(requestEntity.getParams());
        history.setSystemName(requestEntity.getSystemName());
        history.setClientIp(requestEntity.getClientIp());
        history.setBizType(requestEntity.getBizType());
        history.setSysName(requestEntity.getSysName());
        history.setProductType(requestEntity.getProductType());
        history.setPhaseName(requestEntity.getPhaseName());
        history.setApplyNoType(requestEntity.getApplyNoType());
        history.setApplyNo(requestEntity.getApplyNo());
        String idCard = requestEntity.getParams().getString("idCard");
        String mobile = requestEntity.getParams().getString("mobile");
        history.setIdCard(idCard);
        history.setMobile(mobile);
        history.setCrNo(TracerContextUtil.getTraceId());
        history.setQueryTime(DateUtils.transferLongToDate(DateUtils.YYYY_MM_DD_HH_MM_SS, requestEntity.getReqTimestamp()));
        return history;
    }

    /**
     * 保存查询历史
     *
     * @param request
     */
    private void saveQueryHistory(CreditQueryRequest request, Response response) {
        try {
            creditQueryService.saveQueryHistory(request, response);
            //保存认证历史
            authHistoryService.saveAuthHistory(request, response);
        } catch (Exception e) {
            logger.error("保存查询记录异常,msg{}",e);
        }
    }
}